package gttp.nspa.server.dao;

import static com.googlecode.objectify.ObjectifyService.*;

import java.util.Iterator;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.api.datastore.Query.Filter;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import com.googlecode.objectify.cmd.Query;

public abstract class DAO<E> {

    abstract Class<E> entityType();

    public static <E> DAO<E> create(final Class<E> entityType) {
        return new DAO<E>() {

            Class<E> entityType() {
                return entityType;
            }
        };
    }

    public E load(long id) {
        return ofy().load().type(entityType()).id(id).now();
    }

    public Map<Long, E> load(Iterable<Long> ids) {
        return ofy().load().type(entityType()).ids(ids);
    }

    private Query<E> createQuery(int offset, int limit, String[] orders) {
        Query<E> query = ofy().load().type(entityType()).offset(offset);

        if (limit > 0)
            query = query.limit(limit);

        if (orders != null) {
            for (String order : orders) {
                query = query.order(order);
            }
        }
        return query;
    }

    public Iterator<E> load(int offset, int limit, String[] orders) {
        Query<E> query = createQuery(offset, limit, orders);
        return query.iterator();
    }

    public Iterator<E> load(int offset, int limit, String[] orders, String filter, Object filterVal) {
        Query<E> query = createQuery(offset, limit, orders);

        if (StringUtils.isNotEmpty(filter))
            query = query.filter(filter, filterVal);

        return query.iterator();
    }

    public Iterator<E> load(int offset, int limit, String[] orders, Filter filter) {
        Query<E> query = createQuery(offset, limit, orders);

        if (filter != null)
            query = query.filter(filter);

        return query.iterator();
    }

    private Query<E> createQuery(Cursor startAt, String[] orders) {
        Query<E> query = ofy().load().type(entityType()).startAt(startAt);

        if (orders != null) {
            for (String order : orders) {
                query = query.order(order);
            }
        }

        return query;
    }

    public Iterator<E> load(Cursor startAt) {
        return ofy().load().type(entityType()).startAt(startAt).iterator();
    }

    public Iterator<E> load(Cursor startAt, String[] orders) {
        Query<E> query = createQuery(startAt, orders);
        return query.iterator();
    }

    public Iterator<E> load(Cursor startAt, String[] orders, String filter, Object filterVal) {
        Query<E> query = createQuery(startAt, orders);

        if (StringUtils.isNotEmpty(filter))
            query = query.filter(filter, filterVal);

        return query.iterator();
    }

    public Iterator<E> load(Cursor startAt, String[] orders, Filter filter) {
        Query<E> query = createQuery(startAt, orders);

        if (filter != null)
            query = query.filter(filter);

        return query.iterator();
    }

    public E loadByKey(Key<E> key) {
        return ofy().load().key(key).now();
    }

    public Map<Key<E>, E> loadByKeys(Iterable<Key<E>> keys) {
        return ofy().load().keys(keys);
    }

    public E loadByRef(Ref<E> ref) {
        return ofy().load().ref(ref).now();
    }

    public Map<Key<E>, E> loadByRefs(Iterable<Ref<E>> refs) {
        return ofy().load().refs(refs);
    }

    public Key<E> save(E entity) {
        return ofy().save().entity(entity).now();
    }

    public Map<Key<E>, E> save(Iterable<E> entities) {
        return ofy().save().entities(entities).now();
    }

    public void delete(long id) {
        ofy().delete().type(entityType()).id(id).now();
    }

    public void delete(Iterable<Long> ids) {
        ofy().delete().type(entityType()).ids(ids);
    }

    public void deleteByKey(Key<E> key) {
        ofy().delete().key(key).now();
    }

    public void deleteByKeys(Iterable<Key<E>> keys) {
        ofy().delete().keys(keys).now();
    }
}
